package com.jt.aspect;

import com.jt.annotation.RequiredLog;
import com.jt.pojo.Log;
import com.jt.service.RemoteLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Date;

@Aspect/**描述此类为一个切面类型，在此类中可以定义：
 1.切入点（切入扩展逻辑的位置~例如权限控制，日志记录，事物处理），
 2.通知方法（在切入点对应的目标方法执行前后要执行逻辑需要写到这样的方法中），在此注解描述的类中，通过@Before,@After,@Around*/
@Component
public class LogAspect {
    @Pointcut("@annotation(com.jt.annotation.RequiredLog)")
    public void doLog(){}

    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable{
        int status=1;
        String error=null;
        long time=0l;
        long t1=System.currentTimeMillis();
        try {
            //手动调用目标执行链(这个执行链中包含切入点方法~目标方法)
            Object result = joinPoint.proceed();
            long t2=System.currentTimeMillis();
            time=t2-t1;
            return result;
        }catch (Throwable e){
            long t3=System.currentTimeMillis();
            time=t3-t1;
            status=0;
            error=e.getMessage();
            throw e;
        }finally {
            saveLog(joinPoint,time,status,error);
        }
    }
    private void saveLog(ProceedingJoinPoint joinPoint,long time,
                         int status,String error)throws Throwable{
        //1.获取目标对象类型(切入点方法所在类的类型)
        Class<?> targetClass = joinPoint.getTarget().getClass();
        //2.获取目标方法
        //2.1获取方法签名(包含方法信息,....)
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        //2.2获取方法对象
        Method targetMethod=
                targetClass.getDeclaredMethod(signature.getName(),signature.getParameterTypes());
        //3.获取方法上的RequiredLog注解内容
        //3.1获取目标方法上注解
        RequiredLog requiredLog=targetMethod.getAnnotation(RequiredLog.class);
        //3.2获取注解中的内容(这个内容为我们定义的操作名)
        String operation=requiredLog.value();
        //4.将用户行为日志,封装到Log对象
        Log logInfo=new Log();
//        logInfo.setIp("192.168.1.100");//后续获取
        //获取ip(从当前线程获取request对象，然后基于request获取ip地址)
        ServletRequestAttributes requestAttributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String ip = requestAttributes.getRequest().getRemoteAddr();
        logInfo.setIp(ip);
        //获取登录用户信息(参考了Security官方的代码)
        String username=(String)
                SecurityContextHolder.getContext()
                        .getAuthentication()
                        .getPrincipal();//用户身份
        logInfo.setUsername(username);
        logInfo.setOperation(operation);
        logInfo.setMethod(targetClass.getName()+"."+targetMethod.getName());
        logInfo.setParams(new ObjectMapper().writeValueAsString(joinPoint.getArgs()));
        logInfo.setTime(time);
        logInfo.setStatus(status);
        logInfo.setError(error);
        logInfo.setCreatedTime(new Date());
        //将日志对象通过feign方式传递到远程sso-system服务
        logService.insertLog(logInfo);
//        System.out.println("logInfo="+logInfo);
    }
    @Autowired
    private RemoteLogService logService;



}
